<?php

namespace Aspic;

/**
 * Aspic Application loader is a simple way to start the application (main)
 *
 * @use AspicModule
 *
 * @Author Mallory Dessaintes <mdessaintes@gmail.com>
 * @version 1.0
 */
abstract class AbstractAppLoader extends AbstractBase {

	const APP_NAMESPACE = 'App';

	const DEFAULT_MODULE_NAME = 'main';

	const DEFAULT_CONTROLLER_NAME = 'index';

	const DEFAULT_ACTION_NAME = 'index';

	const INIT_METHOD_NAME = 'init';

	const END_METHOD_NAME = 'end';

	/**
	 * Enable ActionsAccessControl or not
	 */
	const USE_AAC = false;

	const MODULE_CLASS = 'Aspic\\Module';

	protected static $_routeObject = null;

	protected static $_directRouteObject = null;

	protected static $_aac; // Action Access Control

	/**
	 * @var array module / controller / action to load when AAC control failed
	 */
	protected static $_accessDeniedPath = array(
		'module'	 => self::DEFAULT_MODULE_NAME,
		'controller' => self::DEFAULT_CONTROLLER_NAME,
		'action'	 => 'accessDenied',
	);

	/**
	 * @var array module / controller / action to load when not route is found
	 */
	protected static $_notFoundPath = array(
		'module'	 => self::DEFAULT_MODULE_NAME,
		'controller' => self::DEFAULT_CONTROLLER_NAME,
		'action'	 => 'notFound',
	);

	protected static function init() {

	}

	protected static function end() {

	}

	public static function run() {
		if (static::USE_AAC) {
			static::initAAC();
		}

		$directActionData = static::getActionData(static::getDirectRouteObject(), false);

		if ($directActionData) {
			$data = $directActionData;
		}
		else {
			call_user_func('static::' . static::INIT_METHOD_NAME);

			$data = static::getActionData();
		}

		$module = $data['module'];
		$controller = $data['controller'];
		$action = $data['action'];
		$params = Util::ifSet($data['params'], array());
		$specialParams = Util::ifSet($data['specialParams'], array());
		$actionPath = $module . '/' . $controller . '/' . $action;

		if (static::USE_AAC == false OR static::actionAccessControlPass($actionPath)) {
			static::loadModule($module, $controller, $action, $params, $specialParams);
		}
		else {
			static::loadAccessDeniedModule($data);
		}

		if (!isset($actionPath)) {
			call_user_func('static::' . static::END_METHOD_NAME);
		}
	}

	public static function loadAccessDeniedModule($deniedData) {
		$module = static::$_accessDeniedPath['module'];
		$controller = static::$_accessDeniedPath['controller'];
		$action = static::$_accessDeniedPath['action'];
		$specialParams = $deniedData['specialParams'];
		$params = $deniedData['params'];

		$specialParams['accessDeniedFor'] = array(
			'role'			 => static::getAAC()->getCurrentRole(),
			'module'		 => $deniedData['module'],
			'controller'	 => $deniedData['controller'],
			'action'		 => $deniedData['action'],
			'params'		 => $deniedData['params'],
			'specialParams'	 => $deniedData['specialParams'],
		);

		static::loadModule($module, $controller, $action, $params, $specialParams);
	}

	public static function initAAC() {
		static::$_aac = new Security\ActionsAccessControl;

		return static::getAAC();
	}

	public static function getAAC() {
		if (is_null(static::$_aac)) {
			static::initAAC();
		}

		return static::$_aac;
	}

	public static function actionAccessControlPass($actionPath) {
		$aac = static::getAAC();

		return $aac->isGrantedFromPath($actionPath);
	}

	/**
	 * Load a module from set pathNs (getPathNs())
	 */
	public static function loadModule($module, $controller = '', $action = '', array $params = array(), array $specialParams = array()) {
		return call_user_func(array(static::MODULE_CLASS, 'loadModule'), $module, $controller, $action, $params, $specialParams, static::APP_NAMESPACE);
	}

	/**
	 * Return the url based on given route name from params
	 */
	public static function getUrlFromRoute($routeName, array $params = array(), $encodeParams = true) {
		$url = Request::getScriptBaseUrl();

		// Looking for route in direct routes
		if (static::$_directRouteObject->routeExists($routeName)) {
			$route = static::$_directRouteObject->getRouteByName($routeName);
		}
		// If not found, looking in "normal" routes
		else {
			$route = static::$_routeObject->getRouteByName($routeName);
		}

		$url .= '/' . $route->reverseRoute($params, $encodeParams);

		return $url;
	}

	/**
	 * Return the route action selected with Route\RouteManager->process()
	 * Do not work for route in direct route object
	 */
	public static function getCurrentActionData() {
		return static::getRouteObject()->getCurrentActionData();
	}

	/**
	 * Return the name of the last $_routeObject selected route by process method
	 * * Do not work for route in direct route object
	 */
	public static function getCurrentRouteName() {
		return static::$_routeObject->getCurrentRouteName();
	}

	/**
	 * Have to return an array with keys : module, controller, action,  specialParams (optionnal) (http request)
	 * @param bool $returnNotFoundRoute If true, it will return that action data for not found route, else return null
	 * @param bool $setGetParams Define if the params would be set in the _GET array or not
	 */
	protected static function getActionData($routeManager = null, $returnNotFoundRoute = true, $setGetParams = true) {
		if (is_null($routeManager)) {
			$routeManager = static::getRouteObject();
		}

		$arrPath['specialParams'] = array(); // To know which specialParams could be set see Controller loadAction method
		// process(true) to register action params in _GET array
		$route = $routeManager->process();

		if ($route) {
			$routeData = $route->parse($routeManager->getSource()); // = array('data' => 'module/controller/action', 'params' => array(...)))

			$routePathExplode = explode('/', $routeData['data']);

			$arrPath['params'] = $routeData['params'];

			if ($setGetParams) {
				Request::registerGetParams($routeData['params']);
			}

			if (count($routePathExplode) == 3) {
				list($module, $controller, $action) = $routePathExplode;

				$arrPath['module'] = $module;
				$arrPath['controller'] = $controller;
				$arrPath['action'] = $action;

				if (is_null($arrPath['module'])) {
					$arrPath['module'] = static::DEFAULT_MODULE_NAME;
				}

				if (is_null($arrPath['controller'])) {
					$arrPath['controller'] = static::DEFAULT_CONTROLLER_NAME;
				}

				if (is_null($arrPath['action'])) {
					$arrPath['action'] = static::DEFAULT_ACTION_NAME;
				}
			}
		}
		elseif ($returnNotFoundRoute) {
			$arrPath = static::$_notFoundPath;
		}
		else {
			$arrPath = null;
		}

		return $arrPath;
	}

	/**
	 * Create a default route manager object
	 */
	public static function initRouteObject() {
		static::$_routeObject = new Route\RouteManager();

		return static::getRouteObject();
	}

	/**
	 * Return route manager object
	 * @return Route\RouteManager
	 */
	public static function getRouteObject() {
		if (is_null(static::$_routeObject)) {
			static::initRouteObject();
		}

		return static::$_routeObject;
	}

	public static function initDirectRouteObject() {
		static::$_directRouteObject = new Route\RouteManager();

		return static::getDirectRouteObject();
	}

	/**
	 * Direct routes are routes that are treated separately and do not pass in init and end method.
	 * Usefull when redirecting to static file like js or css
	 */
	public static function getDirectRouteObject() {
		if (is_null(static::$_directRouteObject)) {
			static::initDirectRouteObject();
		}

		return static::$_directRouteObject;
	}

}

?>